"use strict"

/***********************************************************************
 *  数组去重
 *
 *  一般需求是给你一个数组，调用去重方法，返回数值副本，副本中没有重复元素。
 *  一般来说，两个元素通过 === 比较返回 true 的视为相同元素，需要去重，
 *  所以，1 和 "1" 是不同的元素，1 和 new Number(1) 是不同的元素，
 *  {} 和 {} 是不同的元素（引用不同）。
 *  （当然如果需求认为 {} 和 {} 算作相同的元素，那么解法就不一样了）
 *
 *  参考网址: http://web.jobbole.com/86360/
 **********************************************************************/


//--------方法一-------------------------------------------------------
function unique(a) {
    var res = [], i, j, len, jLen, item;

    for (i = 0, len = a.length; i < len; i++) {
        item = a[i];

        for (j = 0, jLen = res.length; j < jLen; j++) {
            if (res[j] === item)
                break;
        }

        j === jLen && res.push(item);
    }

    return res;
}

var a = [1, 1, '1', '2', 1];
var ans = unique(a);
console.log(ans); // => [1, "1", "2"]

/**
 * 使用es5的Array.prototype.indexOf
 * @param a
 * @returns {Array}
 */
function unique2(a) {
    var res = [];

    for (var i = 0, len = a.length; i < len; i++) {
        var item = a[i];

        (res.indexOf(item) === -1) && res.push(item);
    }

    return res;
}

ans = unique2(a);
console.log(ans); // => [1, "1", "2"]


/**
 * 使用Array.prototype.filter
 * @param a
 * @returns {Array.<T>|*}
 */
function unique3(a) {
    var res = a.filter(function (item, index, array) {
        return array.indexOf(item) === index;
    });

    return res;
}

ans = unique3(a);
console.log(ans); // => [1, "1", "2"]


//--------方法二-------------------------------------------------------
/**
 * 可以换个思路，将原数组中重复元素的最后一个元素放入结果数组中。
 * @param a
 * @returns {Array}
 */
function unique4(a) {
    var res = [];

    for (var i = 0, len = a.length; i < len; i++) {
        for (var j = i + 1; j < len; j++) {
            // 这一步十分巧妙
            // 如果发现相同元素
            // 则 i 自增进入下一个循环比较
            if (a[i] === a[j])
                j = ++i;
        }

        res.push(a[i]);
    }

    return res;
}

a = [1, 1, '1', '2', 1];
ans = unique4(a);
console.log(ans); // => ["1", "2", 1]


//--------方法三-------------------------------------------------------
/**
 * 将数组用 sort 排序后，理论上相同的元素会被放在相邻的位置，那么比较前后位置的元素就可以了。
 * @param a
 * @returns {Array.<T>}
 */
function unique5(a) {
    return a.concat().sort().filter(function (item, pos, ary) {
        return !pos || item != ary[pos - 1];
    });
}

a = [1, 1, 3, 2, 1, 2, 4];
ans = unique5(a);
console.log(ans); // => [1, 2, 3, 4]


//--------方法四-------------------------------------------------------
/**
 * 用 JavaScript 中的 Object 对象来当做哈希表
 * @param a
 * @returns {Array.<T>|Filtered|*}
 */
function unique6(a) {
    var seen = {};

    return a.filter(function (item) {
        return seen.hasOwnProperty(item) ? false : (seen[item] = true);
    });
}

ans = unique6(a);
console.log(ans); // => [1, 3, 2, 4]

/**
 * Object 的 key 值都是 String 类型，所以对于 1 和 "1" 无法分别，我们可以稍微改进下，将类型也存入 key 中
 * 如果数组元素全部是基础类型的 Number 值，键值对法应该是最高效的
 * @param a
 * @returns {Array}
 */
function unique7(a) {
    var ret = [];
    var hash = {};

    for (var i = 0, len = a.length; i < len; i++) {
        var item = a[i];

        var key = typeof(item) + item;

        if (hash[key] !== 1) {
            ret.push(item);
            hash[key] = 1;
        }
    }

    return ret;
}


a = [1, 1, 3, 2, '4', 1, 2, 4, '1'];
ans = unique7(a);
console.log(ans); // => [1, 3, 2, "4", 4, "1"]

//--------方法五-------------------------------------------------------
function unique8(a) {
    return Array.from(new Set(a));
}

a = [{name: "hanzichi"}, {age: 30}, new String(1), new Number(1)];
ans = unique8(a);
console.log(ans); // => [Object, Object, String, Number]

//--------underscore的实现-------------------------------------------------------

// 最后来看看 underscore 对此的实现方式，underscore 将此封装到了 _.unique 方法中，
// 调用方式为 _.unique(array, [isSorted], [iteratee])。
// 其中第一个参数是必须的，是需要去重的数组，
// 第二个参数可选，如果数组有序，则可以传入布尔值 true，
// 第三个参数可选，如果需要对数组迭代的结果去重，则可以传入一个迭代函数。而数组元素去重是基于 === 运算符的。

var _ = {};
// Produce a duplicate-free version of the array. If the array has already
// been sorted, you have the option of using a faster algorithm.
// Aliased as `unique`.
_.uniq = _.unique = function (array, isSorted, iteratee, context) {
    if (!_.isBoolean(isSorted)) {
        context = iteratee;
        iteratee = isSorted;
        isSorted = false;
    }
    if (iteratee != null) iteratee = cb(iteratee, context);
    var result = [];
    var seen = [];
    for (var i = 0, length = getLength(array); i < length; i++) {
        var value = array[i],
            // 如果指定了迭代函数
            // 则对数组每一个元素进行迭代
            computed = iteratee ? iteratee(value, i, array) : value;

        // 如果是有序数组，则当前元素只需跟上一个元素对比即可
        // 用 seen 变量保存上一个元素
        if (isSorted) {
            if (!i || seen !== computed) result.push(value);
            seen = computed;
            // 如果 seen[] 中没有 computed 这个元素值
        } else if (iteratee) {
            if (!_.contains(seen, computed)) {
                seen.push(computed);
                result.push(value);
            }
        } else if (!_.contains(result, value)) {
            // 如果不用经过迭代函数计算，也就不用 seen[] 变量了
            result.push(value);
        }
    }
    return result;
};